﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace RG.Common.Export
{
    public static class ObjToHtmlExtension
    {
        static readonly Encoding UsedXMLEnconding = Encoding.GetEncoding("utf-16");

        public static Stream ToXmlStream(this object obj)
        {
            //var objStream = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite);
            var objStream = new MemoryStream();
            var xs = new XmlSerializer(obj.GetType());
            xs.Serialize(objStream, obj);
            objStream.Position = 0;

            return objStream;
        }

        public static XmlDocument ToXmlDocument(this object obj)
        {
            using (var stream = obj.ToXmlStream())
            {
                var doc = new XmlDocument();
                doc.Load(stream);
                return doc;
            }
        }

        public static string ToHtml(this object obj, Dictionary<string, object> helpers, string resourceName)
        {

            var xmlWriterSettings = new XmlWriterSettings
            {
                Indent = true,
                Encoding = Encoding.GetEncoding("windows-1251"),
                OmitXmlDeclaration = true
            };
            var xslt = new XslCompiledTransform();
            var xsltArgList = new XsltArgumentList();
            if (helpers != null)
                foreach (var helper in helpers)
                    xsltArgList.AddExtensionObject(helper.Key, helper.Value);

            var asm = Assembly.GetCallingAssembly();
            var xslStream = asm.GetManifestResourceStream(resourceName);

            if (xslStream == null)
                throw new ApplicationException("Нет ресурса " + resourceName);

            var objStream = obj.ToXmlStream();

            // Сюда результат попадет
            var htmlStream = new MemoryStream();

            // Подготавливаем к трансформации все
            var xpathDoc = new XPathDocument(objStream);
            xslStream.Position = 0;
            var reader = XmlReader.Create(xslStream);

            // Загрузим текущий шаблон для трансформации
            xslt.Load(reader);

            var writer = XmlWriter.Create(htmlStream, xmlWriterSettings);
            // Паранойя, но решарпер ругается.
            if (writer == null)
                throw new ApplicationException("writer обломился");

            // Выполняем трансформацию в поток
            xslt.Transform(xpathDoc, xsltArgList, writer);

            htmlStream.Position = 0;
            var sr = new StreamReader(htmlStream, xmlWriterSettings.Encoding);
            return sr.ReadToEnd();
        }

        public static void ToExcel(this IEnumerable data, IEnumerable<IColumnInfo> columns)
        {
            var outFileName = Path.GetTempFileName() + ".xml";
            data.ToExcel(outFileName, columns);
            // тут была попытка писать мягкие переносы в ячейку Excel'я, но...
            //string s;
            //using (var fs = new FileStream(outFileName,FileMode.OpenOrCreate, FileAccess.ReadWrite))
            //{
            //    using (var sr = new StreamReader(fs, Encoding.GetEncoding("windows-1251")))
            //    {
            //        s = sr.ReadToEnd();
            //    }
            //}
            //File.Delete(outFileName);
            //using (var fs = new FileStream(outFileName,FileMode.OpenOrCreate, FileAccess.ReadWrite))
            //{
            //    using (var sw = new StreamWriter(fs, Encoding.GetEncoding("windows-1251")))
            //    {
            //        sw.Write(s.Replace("&amp;#10;", "&#10;"));
            //    }
            //}
            var myProcess = new System.Diagnostics.Process { StartInfo = { FileName = outFileName, UseShellExecute = true } };
            myProcess.Start();
            
        }

        public static void ToExcel(this IEnumerable data, string outFileName, IEnumerable<IColumnInfo> columns)
        {
            var xmlWriterSettings = new XmlWriterSettings
            {
                Indent = false,
                Encoding = Encoding.GetEncoding("windows-1251"),
                OmitXmlDeclaration = false
            };
            using (var stream = new MemoryStream())
            {
                using (var xml = new XmlTextWriter(stream, UsedXMLEnconding))
                {
                    PrepareXML(data, columns, xml);

                    using (var excel = new FileStream(outFileName, FileMode.Create))
                    {
                        TransformXsl(stream, "RG.Common.Export.xmlexcel.xslt", excel, xmlWriterSettings);
                    }
                }
            }
        }

        public static void PrepareXML(this IEnumerable data, IEnumerable<IColumnInfo> columns, XmlTextWriter xml)
        {
            xml.WriteStartDocument();
            xml.WriteStartElement("View");
            xml.WriteStartElement("Columns");
            int i = 0;
            foreach (var column in columns)
            {
                xml.WriteStartElement("Column");
                xml.WriteElementString("Header", column.Header.ToString());
                xml.WriteElementString("Name", "c" + i++);
                xml.WriteElementString("Type", column.ExcelDataType());
                xml.WriteElementString("Width", column.Width == 0 ? "15" : (column.Width*4/5).ToString());
                xml.WriteEndElement();
            }
            xml.WriteEndElement();
            //запись самих данных
            xml.WriteStartElement("Data");
            foreach (var item in data)
            {
                xml.WriteStartElement("Record");
                i = 0;
                foreach (var ci in columns)
                    xml.WriteElementString("c" + i++, ci.GetValueForExcel(item));
                xml.WriteEndElement();
            }
            xml.WriteEndElement();
            xml.WriteEndElement();
            xml.WriteEndDocument();
            xml.Flush();
        }

        public static void PrepareXML(this IEnumerable data, IEnumerable<IColumnInfo> columns, Stream stream)
        {
            using (var xml = new XmlTextWriter(stream, UsedXMLEnconding))
            {
                PrepareXML(data, columns, xml);
            }
        }

        public static void TransformXsl(Stream xmlStream, string xslResourceName, Stream outStream, XmlWriterSettings xmlWriterSettings)
        {
            var xslt = new XslCompiledTransform();
            var asm = Assembly.GetCallingAssembly();
            var xslStream = asm.GetManifestResourceStream(xslResourceName);
            if (xslStream == null)
                throw new ApplicationException("Нет ресурса " + xslResourceName);

            // Подготавливаем к трансформации все
            xmlStream.Position = 0;
            var xmlReader = new XmlTextReader(xmlStream);
            xslStream.Position = 0;
            var reader = XmlReader.Create(xslStream);
            // Загрузим текущий шаблон для трансформации
            xslt.Load(reader);
            var writer = XmlWriter.Create(outStream, xmlWriterSettings);
            // Паранойя, но решарпер ругается.
            if (writer == null)
                throw new ApplicationException("writer обломился");
            // Выполняем трансформацию в поток
            xslt.Transform(xmlReader, writer);
        }
    }
}